#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include "Log.hpp"

using namespace std;


Log lg;

enum{
    SOCKET_ERR=1,
    BIND_ERR
};

uint16_t defaultport=8080;
string defaultip="0.0.0.0";
const int size=1024;


class UdpSever{

public:
        UdpSever(const uint16_t &port=defaultport,const string &ip=defaultip):_sockfd(0),_port(port),_ip(ip),isrunning(false)
        {}

        void Init()
        {
            _sockfd=socket(AF_INET,SOCK_DGRAM,0);
            if(_sockfd<0)
            {
                lg(Fatal,"socket create error, sockfd: %d",_sockfd);
                exit(SOCKET_ERR);
            }
            lg(Info,"socket create success, sockfd: %d",_sockfd);
            struct sockaddr_in local;
            bzero(&local,sizeof(local));
             local.sin_family=AF_INET;
             local.sin_port=htons(_port);
            local.sin_addr.s_addr=inet_addr(_ip.c_str());
             if(bind(_sockfd, (const struct sockaddr *)&local, sizeof(local)) < 0)
                {
                    lg(Fatal,"bind error, errno: %d, err string: %s",errno,strerror(errno));
                        exit(BIND_ERR);
                }
            lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));
        }
        void CheckUser(const struct sockaddr_in &client,const string clientip,uint16_t clientport)
        {
                auto iter=online_user.find(clientip);
                if(iter==online_user.end())
                {
                    online_user.insert({clientip,client});
                    cout<<"["<<clientip<<":"<<clientport<<"] add new user"<<endl;
                }
        }

        void Broadcast(const string &info,const string clientip,uint16_t clientport)
        {
                for(const auto &user:online_user)
                {
                    string message="[";
                    message+=clientip;
                    message+=":";
                    message+=to_string(clientport);
                    message+="]#";
                    message+=info;
                    socklen_t len=sizeof(user.second);
                    sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)(&user.second),len);
                }
        }

        void Run()
        {
                isrunning=true;
                char inbuffer[size];
                while(isrunning)
                {
                    struct sockaddr_in client;
                    socklen_t len=sizeof(client);
                    ssize_t n=recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,(struct sockaddr*)&client,&len);
                    if(n<0)
                    {
                        lg(Warning,"recvfrom error, errno: %d, err string: %s",errno,strerror(errno));
                        continue;;
                    }
                    uint16_t clientport=ntohs(client.sin_port);
                    string clientip=inet_ntoa(client.sin_addr);
                    CheckUser(client,clientip,clientport);
                    string info=inbuffer;
                    Broadcast(info,clientip,clientport);
                }
        }

        ~UdpSever()
        {
            if(_sockfd>0)close (_sockfd);
        }

private:
        int _sockfd;
        string _ip;
        uint16_t _port;
        bool isrunning;
        unordered_map<string,struct sockaddr_in> online_user;
};
